package in.muton.edushare;

import in.muton.edushare.auth.EduShareAuthorizationStrategy;
import in.muton.edushare.auth.roles.FileAccessCheck;
import in.muton.edushare.auth.roles.UnivRoleCheck;
import in.muton.edushare.errorPages.InternalErrorPage;
import in.muton.edushare.jobs.EduShareJobScheduler;
import in.muton.edushare.mailer.MailMonitor;
import in.muton.edushare.url.URLLoader;
import in.muton.wicket.extensions.upload.UploadProcessWebRequest;

import java.io.File;

import javax.servlet.http.HttpServletRequest;

import org.apache.cayenne.access.DataContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.wicket.Application;
import org.apache.wicket.IRequestTarget;
import org.apache.wicket.Request;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.Response;
import org.apache.wicket.RestartResponseAtInterceptPageException;
import org.apache.wicket.Session;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.protocol.http.WebRequest;
import org.apache.wicket.protocol.http.WebResponse;
import org.apache.wicket.protocol.http.servlet.AbortWithHttpStatusException;
import org.apache.wicket.request.RequestParameters;
import org.apache.wicket.request.target.basic.URIRequestTargetUrlCodingStrategy;
import org.apache.wicket.request.target.coding.HybridUrlCodingStrategy;
import org.apache.wicket.request.target.coding.IndexedHybridUrlCodingStrategy;
import org.apache.wicket.request.target.coding.MixedParamUrlCodingStrategy;
import org.apache.wicket.request.target.coding.QueryStringUrlCodingStrategy;
import org.apache.wicket.request.target.resource.ResourceStreamRequestTarget;
import org.apache.wicket.settings.IExceptionSettings;
import org.apache.wicket.util.resource.FileResourceStream;
import org.quartz.JobListener;
import org.quartz.SchedulerException;

import com.edushare.common.constants.URLConstants;
import com.edushare.common.dao.FilesUploadedDAO;
import com.edushare.common.dao.SystemBackupDAO;
import com.edushare.common.exceptions.UnknownFileOwnerTypeException;
import com.edushare.common.global.SystemGlobals;
import com.edushare.common.orm.CayenneORMInterface;
import com.edushare.common.util.SystemUtils;
import com.edushare.common.util.URLUtils;
import com.edushare.common.vo.FilesUploaded;
import com.edushare.common.vo.SystemBackup;

/**
 * Application object for your web application. If you want to run this application without deploying, run the Start class.
 * 
 * @see wicket.myproject.Start#main(String[])
 */
public class EduShareApplication extends WebApplication
{    
    private static Log log = LogFactory.getLog(EduShareApplication.class);
	public EduShareApplication(){}
	
	/**
	 * Gets called when application starts
	 */
	@Override
	protected void init(){
		SystemGlobals.getInstance().setWicketApplication(get());
		
		//Mount various paths
		for(String url:URLLoader.QueryStringUrlEncodingStrategy.keySet()){
			mount(new QueryStringUrlCodingStrategy(url, URLLoader.QueryStringUrlEncodingStrategy.get(url)));
		}
		
		for(String url:URLLoader.IndexedParamUrlCodingStrategy.keySet()){
			mount(new IndexedHybridUrlCodingStrategy(url, URLLoader.IndexedParamUrlCodingStrategy.get(url)));
		}
		
		for(String url:URLLoader.IndexedHybridUrlCodingStrategy.keySet()){
			mount(new IndexedHybridUrlCodingStrategy(url, URLLoader.IndexedHybridUrlCodingStrategy.get(url)));
		}
		
		for(String url:URLLoader.HybridUrlCodingStrategy.keySet()){
			mount(new HybridUrlCodingStrategy(url, URLLoader.HybridUrlCodingStrategy.get(url)));
		}
		
		for(String url:URLLoader.MixedParamUrlCodingStrategy.keySet()){
			mount(new MixedParamUrlCodingStrategy(url, URLLoader.MixedParamUrlCodingStrategy.get(url), new String[]{"0", "1", "2", "3","4"}));
		}
		
		mountFileDownloadURL();
		mountBackupDownloadURL();
		
		//Apply authorization strategy
		EduShareAuthorizationStrategy auth = new EduShareAuthorizationStrategy();
		getSecuritySettings().setAuthorizationStrategy(auth);
		getSecuritySettings().setUnauthorizedComponentInstantiationListener(auth);
		
		//Mount error pages
		getApplicationSettings().setInternalErrorPage(InternalErrorPage.class);
		getExceptionSettings().setUnexpectedExceptionDisplay(IExceptionSettings.SHOW_INTERNAL_ERROR_PAGE);
		
		//Start schedules mailer thread
		JobListener listener = new MailMonitor();
		listener.jobWasExecuted(null, null);
		startJobs();
		
		//Flush backup Status
		CayenneORMInterface cayenneIntf = new CayenneORMInterface();
		cayenneIntf.setDataContext(DataContext.createDataContext());
		new SystemBackupDAO(cayenneIntf).flushAnyRunningBackupProcess();
	}
	
	private void mountBackupDownloadURL() {
		mount(new URIRequestTargetUrlCodingStrategy("/"+URLConstants.BACKUP_DOWNLOAD_PATH){
			@Override
			public IRequestTarget decode(RequestParameters requestParameters) {
				String uri = getURI(requestParameters);
				String[] uriArr = uri.split("/");
				int bkpId = Integer.parseInt(uriArr[0]);
				String fileName = uriArr[1];
				
				SystemBackup backup = new SystemBackupDAO(EduShareSession.get().getSessionORMInterface()).getSystemBackupById(bkpId);
				//validate file's existence
				if(backup==null)
					throw new AbortWithHttpStatusException(404, true);
				//validate accessibility
				boolean isAccessible = false;
				isAccessible = new UnivRoleCheck(backup.getUniversity(), EduShareSession.get().getLoggedInUser()).isAdmin();
				if(isAccessible){
					//open save-as dialogue
					((WebResponse)EduShareRequestCycle.get().getResponse()).setAttachmentHeader(fileName);
					//get output stream and return
					String basePath = SystemUtils.getAbsolutePathFromRelative(backup.getDirectoryPath());
					String path = basePath+"/" + fileName;
					return new ResourceStreamRequestTarget(new FileResourceStream(new File(path)));
				}else{
					throw new RestartResponseAtInterceptPageException(URLLoader.getPageClass(URLConstants.UNAUTHORIZED_PATH));
				}
			}
		});
	}

	private void mountFileDownloadURL() {
		mount(new URIRequestTargetUrlCodingStrategy("/"+URLConstants.FILE_DOWNLOAD_PATH){
			@Override
			public IRequestTarget decode(RequestParameters requestParameters) {
				int fileId = URLUtils.getFileIdFromRelativePath(getURI(requestParameters));
				String version = URLUtils.getVersionFromRelativePath(getURI(requestParameters));
				FilesUploaded file = new FilesUploadedDAO(EduShareSession.get().getSessionORMInterface()).getFileById(fileId);
				//validate file's existence
				if(file==null)
					throw new AbortWithHttpStatusException(404, true);
				//validate accessibility
				boolean isAccessible = false;
				try {
					isAccessible = new FileAccessCheck().isAccessible(EduShareSession.get().getLoggedInUser(), file);
				} catch (UnknownFileOwnerTypeException e) {
					log.error("Unknown File Owner Type", e);
					throw new AbortWithHttpStatusException(404, true);
				}
				if(isAccessible){
					//open save-as dialogue
					((WebResponse)EduShareRequestCycle.get().getResponse()).setAttachmentHeader(file.getFileOrigName());
					//get output stream and return
					String basePath = SystemUtils.getRootDirectoryPath();
					String path = basePath+"/" + file.getFileRelativePath(version);
					return new ResourceStreamRequestTarget(new FileResourceStream(new File(path)));
				}else{
					throw new RestartResponseAtInterceptPageException(URLLoader.getPageClass(URLConstants.UNAUTHORIZED_PATH));
				}
			}
		});
	}

	private void startJobs() {
		try {
			EduShareJobScheduler.getInstance().start();
		} catch (SchedulerException e) {
			log.fatal("************* EXCEPTION IN JOB SCHEDULE **************\n", e);
			e.printStackTrace();
		}	
	}

	@SuppressWarnings("unchecked")
	public Class getHomePage()
	{
		return in.muton.edushare.pages.EduShareLogin.class;
	}
	
	/**
	 * Our custom session should be invoked instead of the standard one
	 */
	@Override
	public Session newSession(Request req, Response res){
		return new EduShareSession(req);
	}
	
	public static EduShareApplication get(){
		return (EduShareApplication)Application.get();
	}
	
	/**
	 * creates a new request cycle that has altered pre and post processing
	 * @param request
	 * @param response
	 * @return
	 */
	@Override
	public RequestCycle newRequestCycle(Request request, Response response) {
		return new EduShareRequestCycle(EduShareApplication.get(), (WebRequest)request,response);
	}
	
	/**
	 * Overrides normal request to be able to serve upload/multi-part requests
	 * This implements differs from UploadWebRequest, in a way that it incorporates the processing part in it as well.  
	 * @param servletRequest
	 * @return
	 */
	@Override
	protected WebRequest newWebRequest(HttpServletRequest servletRequest) {
		return new UploadProcessWebRequest(servletRequest);
	}
}
